/*
 * MPEGSideInfo.h
 *        -- define side information format for MPEG frame
 *
 * Author: Qiu Shuang
 * Date: Mar 2nd, 2011
 */
#ifndef MPEGSIDEINFO_H
#define MPEGSIDEINFO_H

#include <stdio.h>
#include "metadata.h"
#include "MPEGDecodable.h"

#ifdef DEBUG
#include <fstream>
#endif

//#define BUFFER_SIZE     SIDEINFO_SIZE
#define BITS_PER_BYTE   8

/* forward class declaration */
class MPEGFrameHeader;
class MPEGGranule;

class MPEGSideInfo : public MPEGDecodable{
    friend class MPEGFrameData;
public:
    /* construtor */
    MPEGSideInfo(MPEGFrameHeader *header, FILE *mpeg);

    /* destructor */
    ~MPEGSideInfo();

    /*  */
    int decode(const unsigned char *buf);

    /* get decoded side information for MPEG1-layer3
     * will always return null in this case
     */
    const unsigned char *decodedContent() const;

#ifdef DEBUG
    void printSideInfo(std::ofstream &output);
#endif

    /* field accessors */
    unsigned short mainDataBegin();
    bool scalefactorSelection(unsigned int channel, unsigned int cls);
    unsigned short part2_3_length(unsigned int granule, unsigned int channel);
    unsigned short bigValue(unsigned int granule, unsigned int channel);
    unsigned char globalGain(unsigned int granule, unsigned int channel);
    unsigned char scalefactorCompress(unsigned int granule, unsigned int channel);
    bool windowSwitching(unsigned int granule, unsigned int channel);
    unsigned char windowType(unsigned int granule, unsigned int channel);
    bool mixedWindow(unsigned int granule, unsigned int channel);
    unsigned char tableSelect(unsigned int granule, unsigned int channel, unsigned int area);
    unsigned char shortwinGain(unsigned int granule, unsigned int channel, unsigned int area);
    unsigned char region0Count(unsigned int granule, unsigned int channel);
    unsigned char region1Count(unsigned int granule, unsigned int channel);
    unsigned int region0Samples(unsigned int granule, unsigned int channel);
    unsigned int region1Samples(unsigned int granule, unsigned int channel);
    bool preflag(unsigned int granule, unsigned int channel);
    bool scalefacScale(unsigned int granule, unsigned int channel);
    bool count1TableSelect(unsigned int granule, unsigned int channel);
    unsigned int preEmphasis(int criticalBand);

private:
    MPEGFrameHeader *_header;               /* header information of this frame */
    unsigned short _main_data_begin;        /* negative offset in bytes
                                             * specifying where the main
                                             * data for the frame begins. */
    unsigned char _padding_bit;             /* 5 bits for mono, 3 bits for stereo */
    static unsigned char _buffer[SIDEINFO_SIZE];
                                            /* side information buffer */
    bool _scalefactor_selection[CHANNEL][SCALEFACTOR_CLASS];
                                            /* specified for the scalefactor classes
                                             * of each channel if the scalefactors for
                                             * the first granule are reused in the second */
    unsigned short _part2_3_length[GRANULE][CHANNEL];
                                            /* specifies for each channel
                                             * and granule the number of *bits*
                                             * used for scalefactors and Huffman
                                             * encoded samples */
    unsigned short _big_values[GRANULE][CHANNEL];
                                            /* specifies number of sample pairs in the
                                             * big-value region */
    unsigned char _global_gain[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule
                                             * the quantizer step size */
    unsigned char _scalefac_compress[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule
                                             * the number of bits used for the
                                             * scalefactors */
    bool _window_switching[GRANULE][CHANNEL];
                                            /* long window or else, 0 for pure long */
    unsigned char _window_type[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule the
                                             * type of window being used */
    bool _mixed_window[GRANULE][CHANNEL];
                                            /* if mixed windows are used */
    unsigned char _table_select[GRANULE][CHANNEL][PURELONGWIN_COUNTS];
                                            /* specifies for each channel and granule
                                             * and each of the three regions which Huffman
                                             * table is used for encoding */
    unsigned char _shortwin_gain[GRANULE][CHANNEL][SUBBLOCK_COUNTS];
                                            /* a gain offset from the global_gain */
    unsigned char _region0_samples[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule the
                                             * number of samples in region0 */
    unsigned char _region1_samples[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule the
                                             * number of samples in region1 */
    bool _preflag[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule
                                             * additional high frequency amplification */
    bool _scalefac_scale[GRANULE][CHANNEL];
                                            /* specifies for each channel and granule
                                             * the quantization of the scalefactors */
    bool _count1_table_select[GRANULE][CHANNEL];
                                            /* which Huffman table is used for the
                                             * count1-region */

    FILE *_mpeg;                            /* MPEG file */
    int _bits_read;                         /* number of bits in buffer has read */

    unsigned int readbits(int num);         /* read number of bits from MPEG file */

    static const unsigned int PREEMPHASIS_TABLE[CRITICAL_BANDS];
};

inline unsigned short MPEGSideInfo::mainDataBegin(){
    return _main_data_begin;
}

inline bool MPEGSideInfo::scalefactorSelection(unsigned int channel, unsigned int cls){
    return _scalefactor_selection[channel][cls];
}

inline unsigned short MPEGSideInfo::part2_3_length(unsigned int granule, unsigned int channel){
    return _part2_3_length[granule][channel];
}

inline unsigned short MPEGSideInfo::bigValue(unsigned int granule, unsigned int channel){
    return _big_values[granule][channel];
}

inline unsigned char MPEGSideInfo::globalGain(unsigned int granule, unsigned int channel){
    return _global_gain[granule][channel];
}

inline unsigned char MPEGSideInfo::scalefactorCompress(unsigned int granule, unsigned int channel){
    return _scalefac_compress[granule][channel];
}

inline bool MPEGSideInfo::windowSwitching(unsigned int granule, unsigned int channel){
    return _window_switching[granule][channel];
}

inline unsigned char MPEGSideInfo::windowType(unsigned int granule, unsigned int channel){
    return _window_type[granule][channel];
}

inline bool MPEGSideInfo::mixedWindow(unsigned int granule, unsigned int channel){
    return _mixed_window[granule][channel];
}

inline unsigned char MPEGSideInfo::tableSelect(unsigned int granule, unsigned int channel, unsigned int area){
    return _table_select[granule][channel][area];
}

inline unsigned char MPEGSideInfo::shortwinGain(unsigned int granule, unsigned int channel, unsigned int area){
    return _shortwin_gain[granule][channel][area];
}

inline unsigned char MPEGSideInfo::region0Count(unsigned int granule, unsigned int channel){
    return _region0_samples[granule][channel];
}

inline unsigned char MPEGSideInfo::region1Count(unsigned int granule, unsigned int channel){
    return _region1_samples[granule][channel];
}

inline bool MPEGSideInfo::preflag(unsigned int granule, unsigned int channel){
    return _preflag[granule][channel];
}

inline bool MPEGSideInfo::scalefacScale(unsigned int granule, unsigned int channel){
    return _scalefac_scale[granule][channel];
}

inline bool MPEGSideInfo::count1TableSelect(unsigned int granule, unsigned int channel){
    return _count1_table_select[granule][channel];
}

inline unsigned int MPEGSideInfo::preEmphasis(int criticalBand){
    return PREEMPHASIS_TABLE[criticalBand];
}
#endif